package com.common;

import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PojoUtil {

    private static final String PREFIX = "get";
    private static final String SEPARATOR = "，";

    /**
     * 比较2个同类型的pojo实例对象，输出值不同的字段数据；只能比较String和基础数据类型的字段
     *
     * @param oldObject
     * @param newObject
     * @param excludeFields 排除字段，多个字段用英文逗号隔开
     * @param mustFields    字段值相同也要输出的字段
     * @return
     */
    public static String compareDifferent(Object oldObject, Object newObject, String excludeFields, String... mustFields) {
        if (null == oldObject || null == newObject) {
            return null;
        }
        if (!oldObject.getClass().getName().equals(newObject.getClass().getName())) {
            throw new RuntimeException("对象类型不相同");
        }
        if (isBaseDataTypeOrString(oldObject)) {
            throw new RuntimeException("对象类型不能为基础数据类型");
        }
        List<String> excludeFieldList = new ArrayList<>();
        if (StringUtils.isNotBlank(excludeFields)) {
            String[] excludeFieldArray = excludeFields.split(",");
            for (int i = 0; i < excludeFieldArray.length; i++) {
                if (StringUtils.isNotBlank(excludeFieldArray[i])) {
                    excludeFieldList.add(excludeFieldArray[i].trim());
                }
            }
        }
        if (excludeFieldList.size() > 0 && null != mustFields && mustFields.length > 0) {
            for (String mustField : mustFields) {
                if (excludeFieldList.contains(mustField)) {
                    throw new RuntimeException(mustField + "不能同时位于排除字段和包含字段中");
                }
            }
        }
        List<String> mustOutFieldList = new ArrayList<>();
        if (null != mustFields && mustFields.length > 0) {
            mustOutFieldList.addAll(Arrays.asList(mustFields));
        }
        Class<?> c = oldObject.getClass();
        List<Method> mustMethodList = new ArrayList<>();
        List<Method> otherMethodList = new ArrayList<>();
        Method[] methods = c.getMethods();
        Map<String, String> map = new HashMap<>();
        if (null != methods && methods.length > 0) {
            String methodName;
            for (Method method : methods) {
                methodName = method.getName();
                if (methodName.startsWith(PREFIX)) {
                    String field = method2Field(methodName, PREFIX);
                    if (excludeFieldList.size() > 0 && excludeFieldList.contains(field)) {
                        continue;
                    }
                    map.put(methodName, field);
                    if (mustOutFieldList.contains(field)) {
                        mustMethodList.add(method);
                    } else {
                        otherMethodList.add(method);
                    }
                }
            }
        }
        StringBuilder sb0 = new StringBuilder();
        try {
            for (Method method : mustMethodList) {
                sb0.append(map.get(method.getName())).append("：").append(wrap(method.invoke(oldObject))).append("->").append(wrap(method.invoke(newObject))).append(SEPARATOR);
            }
            Object oldValue;
            Object newValue;
            for (Method method : otherMethodList) {
                oldValue = method.invoke(oldObject);
                newValue = method.invoke(newObject);
                if (null != oldValue && null != newValue && isBaseDataTypeOrString(oldValue)) {
                    if (!oldValue.equals(newValue)) {
                        sb0.append(map.get(method.getName())).append("：").append(wrap(oldValue)).append("->").append(wrap(newValue)).append(SEPARATOR);
                    }
                } else if (null != oldValue && isBaseDataTypeOrString(oldValue)) {
                    sb0.append(map.get(method.getName())).append("：").append(wrap(oldValue)).append("->").append(wrap(newValue)).append(SEPARATOR);
                } else if (null != newValue && isBaseDataTypeOrString(newValue)) {
                    sb0.append(map.get(method.getName())).append("：").append(wrap(oldValue)).append("->").append(wrap(newValue)).append(SEPARATOR);
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        if (sb0.toString().endsWith(SEPARATOR)) {
            sb0 = sb0.deleteCharAt(sb0.length() - SEPARATOR.length());
        }
        return sb0.toString();
    }

    /**
     * 获取pojo对象字段不为空的字段的值
     *
     * @param object
     * @param excludeFields 排除字段，多个字段用英文逗号隔开
     * @param mustFields
     * @return
     */
    public static String notNull(Object object, String excludeFields, String... mustFields) {
        if (null == object) {
            return null;
        }
        if (isBaseDataTypeOrString(object)) {
            throw new RuntimeException("对象类型不能为基础数据类型");
        }
        List<String> excludeFieldList = new ArrayList<>();
        if (StringUtils.isNotBlank(excludeFields)) {
            String[] excludeFieldArray = excludeFields.split(",");
            for (int i = 0; i < excludeFieldArray.length; i++) {
                if (StringUtils.isNotBlank(excludeFieldArray[i])) {
                    excludeFieldList.add(excludeFieldArray[i].trim());
                }
            }
        }
        if (excludeFieldList.size() > 0 && null != mustFields && mustFields.length > 0) {
            for (String mustField : mustFields) {
                if (excludeFieldList.contains(mustField)) {
                    throw new RuntimeException(mustField + "不能同时位于排除字段和包含字段中");
                }
            }
        }
        List<String> mustOutFieldList = new ArrayList<>();
        if (null != mustFields && mustFields.length > 0) {
            mustOutFieldList.addAll(Arrays.asList(mustFields));
        }
        Class<?> c = object.getClass();
        List<Method> mustMethodList = new ArrayList<>();
        List<Method> otherMethodList = new ArrayList<>();
        Method[] methods = c.getMethods();
        Map<String, String> map = new HashMap<>();
        if (null != methods && methods.length > 0) {
            String methodName;
            for (Method method : methods) {
                methodName = method.getName();
                if (methodName.startsWith(PREFIX)) {
                    String field = method2Field(methodName, PREFIX);
                    if (excludeFieldList.size() > 0 && excludeFieldList.contains(field)) {
                        continue;
                    }
                    map.put(methodName, field);
                    if (mustOutFieldList.contains(field)) {
                        mustMethodList.add(method);
                    } else {
                        otherMethodList.add(method);
                    }
                }
            }
        }
        StringBuilder sb0 = new StringBuilder();
        try {
            for (Method method : mustMethodList) {
                sb0.append(map.get(method.getName())).append("=").append(wrap(method.invoke(object))).append(SEPARATOR);
            }
            Object value;
            for (Method method : otherMethodList) {
                value = method.invoke(object);
                if (null != value && isBaseDataTypeOrString(value)) {
                    sb0.append(map.get(method.getName())).append("=").append(wrap(method.invoke(object))).append(SEPARATOR);
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        if (sb0.toString().endsWith(SEPARATOR)) {
            sb0 = sb0.deleteCharAt(sb0.length() - SEPARATOR.length());
        }
        return sb0.toString();
    }

    /**
     * 判断object是否为基础数据类型或者为String类型
     *
     * @param object
     * @return
     */
    private static boolean isBaseDataTypeOrString(Object object) {
        if (object instanceof Byte || object instanceof Short || object instanceof Integer || object instanceof Long || object instanceof String ||
                object instanceof Float || object instanceof Double || object instanceof Boolean || object instanceof Character || object instanceof Number) {
            return true;
        }
        return false;
    }

    /**
     * 方法名转变为字段名
     *
     * @param str
     * @param prefix
     * @return
     */
    private static String method2Field(String str, String prefix) {
        if (null != str && !"".equals(str.trim())) {
            if (null != prefix && !"".equals(prefix)) {
                if (str.startsWith(prefix)) {
                    int prefixLen = prefix.length();
                    str = str.substring(prefixLen);
                    str = str.substring(0, 1).toLowerCase() + str.substring(1);
                }
            }
        }
        return str;
    }

    private static Object wrap(Object o) {
        if (o instanceof String) {
            return "'" + o + "'";
        }
        return o;
    }

}